-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert StateT to IndexedStateT #1775
Conversation
1bbbccf
to
9653863
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking providing a StateT implemented in IndexedStateT to minimize API breakage, I.e people currently using StateT would need little to none code change in migration.
👍 for keeping a |
Could you have a look at `package.scala`? I added a type alias and kept the StateT companion object around.
…________________________________
From: andy scott <notifications@github.com>
Sent: Monday, July 31, 2017 7:35:32 AM
To: typelevel/cats
Cc: Itamar Ravid; Author
Subject: Re: [typelevel/cats] Convert StateT to IndexedStateT (#1775)
👍 for keeping a StateT around, even if it's just backed by or aliased to IndexedStateT.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub<#1775 (comment)>, or mute the thread</~https://github.com/notifications/unsubscribe-auth/AARSxJPRTVrqDIQOZtocwIwlAFkNEiE4ks5sTVmUgaJpZM4OnvN_>.
|
Aah yes. That seems like it'll do the trick! Thanks. |
9653863
to
c001d9c
Compare
Codecov Report
@@ Coverage Diff @@
## master #1775 +/- ##
==========================================
+ Coverage 95.2% 95.35% +0.15%
==========================================
Files 248 248
Lines 4379 4396 +17
Branches 118 116 -2
==========================================
+ Hits 4169 4192 +23
+ Misses 210 204 -6
Continue to review full report at Codecov.
|
c001d9c
to
289c538
Compare
Coverage should hopefully improve after the last push. Any thoughts about the discussion points above? |
def transformF[G[_], B](f: F[(S, A)] => G[(S, B)])(implicit F: FlatMap[F], G: Applicative[G]): StateT[G, S, B] = | ||
StateT(s => f(run(s))) | ||
def transformF[G[_], B, SC](f: F[(SB, A)] => G[(SC, B)])(implicit F: FlatMap[F], G: Applicative[G]): IndexedStateT[G, SA, SC, B] = | ||
IndexedStateT(s => f(run(s))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not covered, maybe a doctest?
override def ap[A, B](ff: StateT[F, S, A => B])(fa: StateT[F, S, A]): StateT[F, S, B] = | ||
StateT[F, S, B]((s: S) => | ||
override def ap[A, B](ff: IndexedStateT[F, S, S, A => B])(fa: IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, B] = | ||
IndexedStateT[F, S, S, B]((s: S) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not covered, which is a bit surprising. Haven't go the time to investigate further.
My thoughts on your points for discussion.
|
Thanks for your comments @kailuowang. I'll address the feedback regarding coverage. Regarding the functions in the companion object, I'll move the common ones into a Regarding transformS - it's currently on the |
@kailuowang addressed your comments |
checkAll("IndexedStateT[ListWrapper, Int, Int, Int]", AlternativeTests[IndexedStateT[ListWrapper, Int, Int, ?]](SA).monoidK[Int]) | ||
checkAll("IndexedStateT[ListWrapper, Int, Int, Int]", ApplicativeTests[IndexedStateT[ListWrapper, Int, Int, ?]](SA).applicative[Int, Int, Int]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AlternativeTests
already runs the tests from ApplicativeTests
, is there a reason to run them explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm thanks. I wasn't aware of that. Was trying to solve the missing coverage for the ap
override on the Alternative
instance, but I now realize that this is pointless as there shouldn't be explicit overrides for Applicative
methods for StateT
. @djspiewak removed them in #1735.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm going to just get rid of this override.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can remove ap
here as it is needed to implement the Alternative
instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, to solve it I added with IndexedStateTMonad
to the instantiation in the instances trait
* `StateT[F, S, A]` is similar to `Kleisli[F, S, A]` in that it takes an `S` | ||
* argument and produces an `A` value wrapped in `F`. However, it also produces | ||
* an `S` value representing the updated state (which is wrapped in the `F` | ||
* context along with the `A` value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move the old StateT
documentation to the StateT
type alias in package.scala?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
So how does this look to you @kailuowang @peterneyens? I'll start working on |
def empty[A]: StateT[F, S, A] = | ||
StateT.lift[F, S, A](G.empty[A])(G) | ||
|
||
override def ap[A, B](ff: StateT[F, S, A => B])(fa: StateT[F, S, A]): StateT[F, S, B] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am curious why remove this override?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The specific overrides for Applicative were removed in #1735, so my reasoning was that this override wasn't necessary either, and it suffices to use the implementation provided by the Monad instance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, you made IndexedStateTAlternative
require mixing with a Monad
instance trait. Left some comment below.
implicit def catsDataAlternativeForStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[StateT[F, S, ?]] = | ||
new StateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } | ||
private[data] sealed trait IndexedStateTInstances extends IndexedStateTInstances1 { | ||
implicit def catsDataAlternativeForIndexedStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[StateT[F, S, ?]] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what if you make this instance an Alternative[StateT[F, S, ?]] with Monad[StateT[F, S, ?]]
and let IndexedStateTAlternative
inherit IndexedStateTMonad
. Since without the ap
, IndexedStateTAlternative
can't be used without mixin with IndexedStateTMonad
Do you still need to override pure
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will try :-) would that approach look cleaner? Personally I have no preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely cleaner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the return type Alternative[StateT[F, S, ?]] with Monad[StateT[F, S, ?]]
here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @kailuowang - I actually changed Alternative
to extend Monad
, so there's no need for the intersection type now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I meant. Since you are in effect returning an Alternative with Monad
, why hide the fact that it's a Monad
? It has a lot more power than the return type suggests. I just felt a bit odd that your return type isn't the actual public type of the instance. Odd code take a bit more time for code reader to grasp, that's all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My reasoning was that this was about implicit search priorities: let the catsDataAlternativeForIndexedStateT
return only an Alternative
instance, and let the catsDataMonadForIndexedStateT
handle the Monad
instance, while hiding the fact that the Alternative
instance in fact uses the Monad
instance.
If you find that returning the intersection type makes more sense, I'll fix that right away :-) Just haven't seen this done anywhere else in the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have examples of returning intersection type.
/~https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/instances/list.scala#L11
IIRC, at this position instance use to be a MonadCombine
which is MonadFilter with Alternative
, so I don't have issues with this instance being a Monad with Alternative
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool! Thanks. Changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for putting up with my nitpicking! I will find time to go through the whole PR tomorrow or the day after.
@iravid , I left a comment, will continue on it later (probably sometime next week though) |
transform { case (s, a) => (s, f(a)) } | ||
|
||
def contramap[S0](f: S0 => SA)(implicit F: Monad[F]): IndexedStateT[F, S0, SB, A] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't this only need Applicative[F]
?
(pure(f), runF).map2(_.andThen(_))
I think does it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! That's awesome, thank you :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Downgraded dimap
too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the noise. Turns out it's just Functor
that's needed for both.
Any other comments anyone? |
Any additional feedback would be appreciated, folks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, sorry it took so long for me to finish my review.
No worries. As @non mentioned in the RWST PR, I'd like to rename the files once reviews are done. So please hold off on merging after another reviewer has finished reviewing :-) |
private[data] sealed trait StateTInstances extends StateTInstances1 { | ||
implicit def catsDataAlternativeForStateT[F[_], S](implicit FM: Monad[F], FA: Alternative[F]): Alternative[StateT[F, S, ?]] = | ||
new StateTAlternative[F, S] { implicit def F = FM; implicit def G = FA } | ||
private[data] sealed trait IndexedStateTInstances extends IndexedStateTInstances1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
per #1879 do you mind change all the instance traits here into abstract class? I am creating a PR to change other classes, but don't want to touch this one to avoid conflict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Ping. This has been sitting here for a while. Any chance we could move this forward? |
@iravid github notification may not reach the people you want to reach ( they got so many of them). maybe you could ping the two other reviewers in gitter cats-dev? |
Good point @kailuowang :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
This looks good to me. Thanks for pushing this through and sorry I hadn't looked sooner.
* an `S` value representing the updated state (which is wrapped in the `F` | ||
* context along with the `A` value. | ||
* | ||
* `IndexedStateT[F, SA, SB, A]` is a stateful computation in a context `F` yielding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you comment about the Indexed
name here? I don't know the context, though I can understand the rest of the comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @johnynek. I refined the scaladoc here. See if that makes it more descriptive.
c6e9c2a
to
6e25fbb
Compare
@kailuowang Let me know if this is sufficient review-wise and I will rename the files before merge :-) |
Okay. Good point about Arrow. I'd now probably say we shouldn't add it since just using Monoid feels rather lawless to me. Not clear the monoid empty is what you want since we don't combine on these values. What do you think? We could keep as you have it, drop Arrow, or make the Arrow def accept the values to use within lift, which means it can't be implicit but at least saves someone the work of implementing it if they want it (although implementing it isn't that much work). |
@johnynek I would lean towards removing it entirely in that case, but I had never used We could keep |
I concur.
Sorry for the goose chase here.
On Sun, Sep 10, 2017 at 19:01 Itamar Ravid ***@***.***> wrote:
@johnynek </~https://github.com/johnynek> I would lean towards removing it
entirely in that case, but I had never used Arrow and friends before, so
I don't have clear intuition here.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1775 (comment)>, or mute
the thread
</~https://github.com/notifications/unsubscribe-auth/AAEJdr7x9JfD_AT8tFMS4NIzH2sFCSqDks5shL6tgaJpZM4OnvN_>
.
--
P. Oscar Boykin, Ph.D. | http://twitter.com/posco | http://pobox.com/~boykin
|
No worries :-)
________________________________
From: P. Oscar Boykin <notifications@github.com>
Sent: Monday, September 11, 2017 8:35:04 AM
To: typelevel/cats
Cc: Itamar Ravid; Mention
Subject: Re: [typelevel/cats] Convert StateT to IndexedStateT (#1775)
I concur.
Sorry for the goose chase here.
On Sun, Sep 10, 2017 at 19:01 Itamar Ravid ***@***.***> wrote:
@johnynek </~https://github.com/johnynek> I would lean towards removing it
entirely in that case, but I had never used Arrow and friends before, so
I don't have clear intuition here.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1775 (comment)>, or mute
the thread
</~https://github.com/notifications/unsubscribe-auth/AAEJdr7x9JfD_AT8tFMS4NIzH2sFCSqDks5shL6tgaJpZM4OnvN_>
.
--
P. Oscar Boykin, Ph.D. | http://twitter.com/posco | http://pobox.com/~boykin
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<#1775 (comment)>, or mute the thread</~https://github.com/notifications/unsubscribe-auth/AARSxH4Zd9tXX_pO29iwukO1zbPIOhpOks5shMaIgaJpZM4OnvN_>.
|
53ea464
to
995d451
Compare
👍 |
Resolves #1773.
I converted this rather mechanically, so please point out any flaws if you notice. The files will be renamed once discussion is done; it's hard to see the diffs once they're renamed.
Points for discussion:
StateTFunctions
to keep versions with less type parameters. This helps because you usually need to write them out due to bad inference in for comprehensions. Does this make sense?transformS
around to minimize breakage, but it seems a bit out of place.SA
parameter, while the Functor instance is on theA
parameter. Compare this toRWST
where the Contravariant instance is for the Reader part.SA, SB
parameters while on RWST it is on the Reader and result parameters. This seems inconsistent.